package cn.net.dckj.common.utils.file.ftp;

import cn.net.dckj.common.exception.file.FileNameLengthLimitExceededException;
import cn.net.dckj.common.exception.file.FileSizeLimitExceededException;
import cn.net.dckj.common.exception.file.InvalidExtensionException;
import cn.net.dckj.common.utils.file.FileUploadUtils;
import cn.net.dckj.common.utils.file.MimeTypeUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPFileFilter;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.StringTokenizer;

/**
 * @author future
 * @Project: jso5
 * @ClassName: FTPUtils.java
 * @Company: JIT Northeast R & D
 * @Description: ftp工具类
 * @date 2014年9月17日
 */
@Component
public class FTPUtils {
    /**
     * 日志类
     */
    private static final Logger log = LogManager.getLogger(FTPUtils.class);

    @Autowired
    private FTPProperties ftpProperties;

    private static FTPUtils ftpUtils = null;

    public static FTPUtils getInstance() {
        if (ftpUtils == null) {
            ftpUtils = new FTPUtils();
        }
        return ftpUtils;
    }

    @PostConstruct
    public boolean init() {
        ftpUtils = this;
        return true;
    }

    /**
     * 获取FTP连接
     *
     * @return
     * @throws Exception
     */
    private FTPClient getClient() throws Exception {
        // FTP客户端对象
        FTPClient ftpClient = new FTPClient();
        ftpClient.setControlEncoding(ftpProperties.getEncoding());
        ftpClient.setConnectTimeout(ftpProperties.getClientTimeout());
        try {
            ftpClient.connect(ftpProperties.getHost(), ftpProperties.getPort());
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                log.warn("FTPServer refused connection");
                return null;
            }
            boolean result = ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());
            ftpClient.setFileType(ftpProperties.getTransferFileType());
            if (!result) {
                log.warn("ftpClient login failed... username is {}", ftpProperties.getUsername());
                ftpClient.logout();
                ftpClient.disconnect();
                return null;
            }
            //设置被动模式
            ftpClient.enterLocalPassiveMode();
            Integer bufferSize = ftpProperties.getBufferSize();
            if (bufferSize != null && bufferSize > 0) {
                ftpClient.setBufferSize(bufferSize);
            }
        } catch (Exception e) {
            log.error("create ftp connection failed...{}", e);
            throw e;
        }
        return ftpClient;
    }

    /**
     * Description: 向FTP服务器上传文件
     *
     * @param remoteFile 上传到FTP服务器上的文件名
     * @param input      本地文件流
     * @return 成功返回true，否则返回false
     * @Version1.0
     */
    public boolean uploadFile(String remoteFile, InputStream input) {
        boolean result = false;
        // FTP客户端对象
        FTPClient ftpClient = null;
        try {
            // 连接FTP服务
            ftpClient = getClient();
            result = ftpClient.storeFile(remoteFile, input);
        } catch (Exception e) {
            log.error("Platform Error:The Object is {} .", this.getClass(), e);
        } finally {
            if (null != input) {
                try {
                    input.close();
                } catch (IOException e) {
                    log.error("Platform Error:The InputStream failed to close at {}.", this.getClass(), e);
                }
            }
            if (null != ftpClient) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("Platform Error:The FTPClient failed to disconnect at {}.", this.getClass(), e);
                }
            }
        }
        return result;
    }

    /**
     * Description: 向FTP服务器上传文件
     *
     * @param remoteFile 上传到FTP服务器上的文件名
     * @param localFile  本地文件
     * @return 成功返回true，否则返回false
     * @Version1.0
     */
    public boolean uploadFile(String remoteFile, String localFile) {
        FileInputStream input = null;
        try {
            input = new FileInputStream(new File(localFile));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return uploadFile(remoteFile, input);
    }

    /**
     * 获取文件输入流
     *
     * @param fileName
     * @return
     * @throws IOException
     */
    public InputStream getFileInputStream(String fileName) throws Exception {
        InputStream in = null;
        // FTP客户端对象
        FTPClient ftpClient = null;
        try {
            // 连接FTP服务
            ftpClient = getClient();
            in = ftpClient.retrieveFileStream(fileName);
        } catch (Exception e) {
            log.error("Platform Error:The Object is {} .", this.getClass(), e);
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error("Platform Error:The InputStream failed to close at {}.", this.getClass(), e);
                }
            }
            if (null != ftpClient) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("Platform Error:The FTPClient failed to disconnect at {}.", this.getClass(), e);
                }
            }
        }
        return in;
    }

    /**
     * Description: 从FTP服务器下载文件
     *
     * @return
     * @Version1.0
     */
    public boolean downFile(String remoteFile, String localFile) throws Exception {
        File file = new File(localFile);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        /*if (!file.exists()) {
            file.createNewFile();
        }*/
        OutputStream os = new FileOutputStream(localFile);
        return downFile(remoteFile, os);
    }

    /**
     * Description: 从FTP服务器下载文件
     *
     * @return
     * @Version1.0
     */
    public boolean downFile(String remoteFile, OutputStream os) throws Exception {
        boolean result = false;
        // FTP客户端对象
        FTPClient ftpClient = null;
        try {
            // 连接FTP服务
            ftpClient = getClient();
            result = ftpClient.retrieveFile(remoteFile, os);
        } catch (Exception e) {
            log.error("Platform Error:The Object is {} .", this.getClass(), e);
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e1) {
                    log.error("Platform Error:The OutputStream failed to close at {}.", this.getClass(), e1);
                }
            }
            if (null != ftpClient) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("Platform Error:The FTPClient failed to disconnect at {}.", this.getClass(), e);
                }
            }
        }
        return result;
    }

    /**
     * 获取ftp目录下的所有文件
     *
     * @param dir
     * @return
     */
    public FTPFile[] getFiles(String dir) throws Exception {
        FTPFile[] files = new FTPFile[0];
        // FTP客户端对象
        FTPClient ftpClient = null;
        try {
            // 连接FTP服务
            ftpClient = getClient();
            files = ftpClient.listFiles(dir);
        } catch (Throwable thr) {
            thr.printStackTrace();
        } finally {
            if (null != ftpClient) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("Platform Error:The FTPClient failed to disconnect at {}.", this.getClass(), e);
                }
            }
        }
        return files;
    }

    /**
     * 获取ftp目录下的某种类型的文件
     *
     * @param dir
     * @param filter
     * @return
     */
    public FTPFile[] getFiles(String dir, FTPFileFilter filter) throws Exception {
        FTPFile[] files = new FTPFile[0];
        // FTP客户端对象
        FTPClient ftpClient = null;
        try {
            // 连接FTP服务
            ftpClient = getClient();
            files = ftpClient.listFiles(dir, filter);
        } catch (Throwable thr) {
            thr.printStackTrace();
        } finally {
            if (null != ftpClient) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("Platform Error:The FTPClient failed to disconnect at {}.", this.getClass(), e);
                }
            }
        }
        return files;
    }

    /**
     * 创建文件夹
     *
     * @param remoteDir
     * @return 如果已经有这个文件夹返回false
     */
    public boolean makeDirectory(String remoteDir) throws Exception {
        boolean result = false;
        // FTP客户端对象
        FTPClient ftpClient = null;
        try {
            // 连接FTP服务
            ftpClient = getClient();
            result = ftpClient.makeDirectory(remoteDir);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != ftpClient) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("Platform Error:The FTPClient failed to disconnect at {}.", this.getClass(), e);
                }
            }
        }
        return result;
    }

    public boolean mkdirs(String dir) throws Exception {
        boolean result = false;
        if (null == dir) {
            return result;
        }
        // FTP客户端对象
        FTPClient ftpClient = null;
        try {
            // 连接FTP服务
            ftpClient = getClient();
            ftpClient.changeWorkingDirectory("/");
            StringTokenizer dirs = new StringTokenizer(dir, "/");
            String temp = null;
            while (dirs.hasMoreElements()) {
                temp = dirs.nextElement().toString();
                //创建目录
                ftpClient.makeDirectory(temp);
                //进入目录
                ftpClient.changeWorkingDirectory(temp);
            }
            ftpClient.changeWorkingDirectory("/");
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != ftpClient) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("Platform Error:The FTPClient failed to disconnect at {}.", this.getClass(), e);
                }
            }
        }
        return result;
    }

    public final String uploadFile(String baseUrl, MultipartFile file) throws IOException {
        try {
            return uploadFile(baseUrl, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, true);
        } catch (Exception e) {
            throw new IOException(e.getMessage(), e);
        }
    }

    public final String uploadFile(String baseUrl, MultipartFile file, boolean changeName) throws IOException {
        try {
            return uploadFile(baseUrl, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, changeName);
        } catch (Exception e) {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 文件上传
     *
     * @param baseUrl          相对应用的基目录
     * @param file             上传的文件
     * @param allowedExtension 上传文件类型
     * @return 返回上传成功的文件名
     * @throws FileSizeLimitExceededException       如果超出最大大小
     * @throws FileNameLengthLimitExceededException 文件名太长
     * @throws IOException                          比如读写文件出错时
     * @throws InvalidExtensionException            文件校验异常
     */
    public String uploadFile(String baseUrl, MultipartFile file, String[] allowedExtension,
                             boolean changeName) throws Exception {
        // 校验文件
        int fileNamelength = file.getOriginalFilename().length();
        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
        }
        FileUploadUtils.assertAllowed(file, allowedExtension);
        // 生成文件名称
        String fileName = FileUploadUtils.extractFilename(file, changeName);
        // 生成文件上传全路径包含文件名
        String pathFile = baseUrl + "/" + fileName;
        // 生成文件上传全路径文件夹
        File f = new File(pathFile);
        String path = f.getParent();
        mkdirs(path);
        // 获取文件名称
        String fName = f.getName();
        String firstFName = fName.split("\\.")[0];
        String suffer = fName.split("\\.")[1];
        int i = 0;
        while (isFileExsits(pathFile)) {
            i++;
            firstFName = firstFName + i;
            fName = firstFName + "." + suffer;
            pathFile = path + File.separator + fName;
        }
        fileName = pathFile.replace("\\", "/");
        uploadFile(fileName, file.getInputStream());
        return fileName;
    }

    /***
     * 判断文件是否存在
     *
     * @param ftpPathFile
     * @return
     */
    public boolean isFileExsits(String ftpPathFile) throws Exception {
        boolean result = false;
        if (null == ftpPathFile) {
            return result;
        }
        // FTP客户端对象
        FTPClient ftpClient = null;
        try {
            // 连接FTP服务
            ftpClient = getClient();
            FTPFile[] files = ftpClient.listFiles(ftpPathFile);
            if (files != null && files.length > 0) {
                result = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != ftpClient) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("Platform Error:The FTPClient failed to disconnect at {}.", this.getClass(), e);
                }
            }
        }
        return result;
    }

    public String getDownloadUrl(String ftpPathName, String fileName) {
        String downloadUrl = "/common/ftpDownload?ftpPathName=" + ftpPathName + "&fileName=" + fileName;
        return downloadUrl;
    }

    public String getPreviewUrl(String ftpPathName) {
        String previewUrl = "";
        try {
            String fileType = ftpPathName.substring(ftpPathName.lastIndexOf(".") + 1);
            fileType = fileType.toLowerCase();
            boolean isAllowedStream = FileUploadUtils.isAllowedExtension(fileType, MimeTypeUtils.ALLOWED_STREAM_EXTENSION);
            if (isAllowedStream) {
                previewUrl = "/common/preview?ftpPathName=" + ftpPathName;
            } else {
                boolean isAllowedPreview = FileUploadUtils.isAllowedExtension(fileType, MimeTypeUtils.ALLOWED_PREVIEW_EXTENSION);
                if (isAllowedPreview) {
                    previewUrl = "/common/previewChangeType?ftpPathName=" + ftpPathName;
                }
            }
        } catch (Exception e) {
            previewUrl = "";
            e.printStackTrace();
        }
        return previewUrl;
    }

    public String getMobilePreviewUrl(String ftpPathName) {
        String previewUrl = "";
        try {
            String fileType = ftpPathName.substring(ftpPathName.lastIndexOf(".") + 1);
            fileType = fileType.toLowerCase();
            boolean isAllowedStream = FileUploadUtils.isAllowedExtension(fileType, MimeTypeUtils.ALLOWED_STREAM_EXTENSION);
            if (isAllowedStream) {
                previewUrl = "/mobile/common/preview?ftpPathName=" + ftpPathName;
            }
        } catch (Exception e) {
            previewUrl = "";
            e.printStackTrace();
        }
        return previewUrl;
    }

}